// Copyright (C) Calum Grant 2008

namespace dynamic
{
	namespace internal
	{
		/// Called to allocate memory in the current apartment.
		DYNAMIC_API void * apartment_malloc(std::size_t bytes);

		/// Call to free memory allocated by apartment_malloc().
		DYNAMIC_API void apartment_free(void *p);

		/// Not implemented - called after each GC for delayed coallescing.
		DYNAMIC_API void apartment_defrag();
	}

	namespace gc
	{
		class gc_object;

		/// The interface to the garbage collector.
		/** \ingroup mem */
		class garbage_collector
		{
		public:
			virtual ~garbage_collector();

			/// The linked list of GCed objects.
			virtual gc_object * chain()=0;

			/// Indicates to the GC that a particular object is reachable.
			virtual void mark_reachable(gc_object*)=0;

			/// Perform a full GC now.
			virtual void collect()=0;

			/// Return the number of managed objects.
			virtual int object_count()=0;

			/// Callback whenever an object is managed by the GC.
			virtual void on_add_object()=0;

			/// Hint to the GC that it can collect now if it's appropriate.
			virtual void try_idle_gc()=0;

			/// Disable all collection.
			/** Call enable() again to re-enable GC.
				This call can be nested, and GC only takes place when the number of enable() calls
				equals the number of disable() calls.
				*/
			virtual void disable()=0;

			/// Called to re-enable GC.
			virtual void enable()=0;

			/// Sets the minimum number of objects that trigger a GC.
			virtual void set_min_objects(int)=0;

			/// Sets when GC will next happen, if calling try_idle_gc().
			/** The default = 20, which means that the heap grows by a factor of 2
				before GC happens. */
			virtual void set_idle_gc_factor(int)=0;

			/// Sets when GC will next happen.
			/** The default = 30, which means that the heap grows by a factor of 3
				before GC kicks in when calling on_add_object() */
			virtual void set_gc_factor(int)=0;
		};


		/// Accesses the garbage collector singleton.
		/** There might be more than one garbage collector so this
			returns the one currently in scope. 
			\ingroup mem*/
		DYNAMIC_API garbage_collector & global_gc();

		/// Creates a new garbage collector.
		/** \ingroup mem */
		garbage_collector * create_gc();

		/// Any object managed by the garbage collector.
		/** \ingroup mem */
		class DYNAMIC_API gc_object
		{
			/// A doubly-linked list of objects.
			gc_object * next, *prev;

			/// The number of root vars referencing this object.
			unsigned root_ref_count : 31;

			/// An internal flag used in the mark/sweep algorithm.
			bool marked : 1;
			
		public:		/** \todo These methods should not be public. */

			/// Links the object to the current garbage colelctor.
			void link();

			/// Links the object to the specified list.
			void link_to(gc_object*);

			/// Removes the object from all lists.
			void unlink();

			/// Used for traversing the list of objects.
			gc_object * next_gc_object() const 
			{ 
				return next; 
			}

		public:
			/// Allocate a new garbage-collected object.
			void * operator new(std::size_t s) 
			{ 
				return internal::apartment_malloc(s); 
			}

			/// Deallocate object.
			void operator delete(void * p) 
			{ 
				return internal::apartment_free(p); 
			}

			/// Copy constructor causes the object to be managed by a GC.
			gc_object(const gc_object&) : root_ref_count(0)
			{
				link();
			}

			/// Assignment does not change GC ownership.
			gc_object & operator=(const gc_object&)
			{
				return *this;
			}

			/// Construct the object without managing it by the GC.
			gc_object(int) : root_ref_count(1)
			{
				next = prev = this;
			}

			/// Default constructor causes the object to be managed by a GC.
			gc_object() : root_ref_count(0)
			{
				link();
			}

			/// When destroyed we remove ourselves from any lists.
			/** Also useful if a derived class constructor throws an exception. */
			virtual ~gc_object() 
			{ 
				unlink(); 
			}

			/// Called prior to destroying the object
			virtual void finalize() { }
			
			/// Called by the garbage collector to indicate that this is a live object.
			virtual void mark() { marked = true; }

			/// Called by the GC to tell an object to mark its children.
			virtual void mark_children(garbage_collector &) =0;

			/// Indicates to the GC whether this is a live object.
			bool is_marked() const 
			{ 
				return marked; 
			}

			/// Called by the GC to set up a sweep.
			virtual void init_mark() 
			{ 
				marked = root_ref_count!=0; 
			}

			/// Mark the object as a root object.
			void add_root_ref() 
			{ 
				++root_ref_count; 
			}

			/// Finished with the object.
			void release_root_ref() { --root_ref_count; }
		};


		/// Used to manage an object which is a root variable.
		/** A root variable is an object which is in the "root set".
			Root variables are never deleted by the garbage collector,
			and are used to seed the search of all live objects.
			\ingroup mem
			*/
		template<typename T>
		class root_ref
		{
			T * object;
		public:
			typedef T value_type;

			/// Keeps a root ref on t.
			root_ref(T * t) : object(t)
			{
				object->add_root_ref();
			}

			/// Keeps a root ref on other.
			root_ref(const root_ref & other) : object(other.object)
			{
				object->add_root_ref();
			}

			/// Releases the root ref.
			~root_ref()
			{
				object->release_root_ref();
			}

			/// Ref other and release current ref.
			root_ref & operator=(root_ref other)
			{
				swap(other);
				return *this;
			}

			/// Access the object.
			T * operator->() const 
			{ 
				return object; 
			}
			
			/// Access the object.
			T & operator*() const 
			{ 
				return *object; 
			}
			
			/// Access the object.
			T * get() const 
			{ 
				return object; 
			}

			/// Swaps with another object.
			void swap(root_ref & other)
			{
				std::swap(object, other.object);
			}
		};
	}
}
